package cmp.viewer.panels;

/***************************************************
 * At least [n] species
 * With DE < [0.05]
 * Include/Exclude of DE values
 * Where lib1 > lib2
 */
import java.sql.ResultSet;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;

import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;

import cmp.main.Globals;
import cmp.viewer.ViewerFrame;
import assem.DBConn;
import cmp.database.DBinfo;
import util.Debug.ErrorReport;
import util.ui.ButtonComboBox;

public class POGQueryDE extends JPanel{
	private static final long serialVersionUID = 1L;
	final private String any = "at least 1";
	final private String all = "All";
	final private String pval = "0.05";
	final private String nsp = "1";
	
	public POGQueryDE (ViewerFrame parentFrame, boolean negDE) {
		try {
			pFrame = parentFrame;
			hasNegDE = negDE;
			DBConn dbc = pFrame.getDBConnection();
			DBinfo info = new DBinfo (dbc, false);
			String [] libs = info.getPOGLib();
			String [] de = info.getPOGDE();
			nSpecies = info.getASMnum();
			
			setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
			setAlignmentX(Component.LEFT_ALIGNMENT);
			setBackground(Color.white);
			
			JPanel dePage = createPanelPage();
			// N and Pvalue input
			JPanel first = firstPanel();
			dePage.add(first);
			dePage.add(Box.createVerticalStrut(5));		
						
			// DE to include
			ieDEcols = new IncludeExclude(de);
			ieDEcols.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));		
			dePage.add(ieDEcols);
			dePage.add(Box.createVerticalStrut(10));
			
			if (hasNegDE) {
				dePage.add(new JLabel("Do not select L1L2 and -L1L2"));
				dePage.add(Box.createVerticalStrut(5));
				dePage.add(new JLabel("where L1L2 is library 1 > library 2"));
				dePage.add(Box.createVerticalStrut(5));
				dePage.add(new JLabel("and -L1L2 is library 1 < library 2"));
			}		
			add(dePage);
		}
		catch (Exception e) {
			ErrorReport.prtReport(e, "Creating DE panel");
		}
	}
	/*****************************************************
	 * Called from POGQueryPanel to get list of IDs that pass the query
	 */
	public String getSQLClusterIDs(int method) {
		String retIdList= null; 
		
		boolean isApplet = pFrame.isApplet();
		
		String [] ieDE = ieDEcols.includeList();
		String pval = txtPval.getText().trim();
		
		if (ieDE == null || pval.length() == 0) {
			summary = ""; 
			return null;
		}
		
		double cutoff = Double.parseDouble(pval);
		double ncutoff = -cutoff;
		boolean bAny = (cmbAnyAll.getSelectedIndex() == 0) ? true : false;
		
		String ntxt = txtNspecies.getText().trim();
		int NspParam = Integer.parseInt(ntxt);
		if (NspParam > nSpecies) NspParam = nSpecies; 
		
		// build where clause and summary string
		String SQLqueryStr="";
		summary = "At least " + NspParam + " species, ";
		summary += (bAny) ? any  : all; 
		summary += " has Pval < " + pval + " for ";
		
		String last = "";
		for (int i=0; i<ieDE.length; i++) {
			summary += " " + ieDE[i];
			
			String sign="up"; 
			if (ieDE[i].startsWith("-")) {
				sign="down";
				ieDE[i] = ieDE[i].substring(1);
			}
					
			String deCol =  "unitrans." + Globals.PRE_DE + ieDE[i];
					
			if (sign.equals("down"))	
				SQLqueryStr +=  " AND (" + deCol + " > " + ncutoff + " AND " + deCol + "< 0)";
			else 
				SQLqueryStr +=  " AND (" + deCol + " < " + cutoff + " AND " + deCol + " >= 0 )";  
			last=ieDE[i];
		}
		
		try {
			DBConn dbc = pFrame.getDBConnection();
			Vector <Integer> PGid = new Vector <Integer> ();					
			Integer [] spCnt = new Integer [nSpecies+1];
			if (NspParam==0) NspParam = nSpecies;
			
			// narrow down number of clusters to look at; create array of PGids
			String taxa = null;
			for (int i=NspParam; i<=nSpecies; i++) {
				if (taxa==null) taxa = "('" + i + "x1" + "','" + i + "xN" + "'";
				else taxa += ",'" + i + "x1" + "','" + i + "xN" + "'";
			}
			taxa += ")";
			
			// get PGid that are correct method and taxa
			String sql = "SELECT PGid FROM pog_groups WHERE taxa IN " + taxa;
			if (method != -1) sql += " AND PMid=" + method;
			ResultSet rs =dbc.executeQuery(sql);
		
			while (rs.next()) { PGid.add(rs.getInt(1)); }
			
			// From these PGid, which pass the rest of filters
			System.err.println("Searching " + PGid.size() + " initial clusters");
			int x=0, add=0;
			for (int i=0; i< PGid.size(); i++) {
				for (int j=0; j< spCnt.length; j++) spCnt[j]=0;
				x++;
				if (!isApplet && x%100==0) 
					System.err.print("  processing #" + x + " found " + add + "\r");
				
				rs = dbc.executeQuery("SELECT unitrans.ASMid, unitrans.UTstr " + 
					" FROM unitrans JOIN pog_members " +
					" WHERE pog_members.PGid= " + PGid.get(i) + 
					" AND unitrans.UTid=pog_members.UTid " +
					SQLqueryStr);
				
				// do N species have filtered DE?
				while (rs.next()) {
					int asmid = rs.getInt(1);
					spCnt[asmid]++;	
				}
				int cnt=0, total=0;
				for (int j=1; j<spCnt.length; j++) {
					if (spCnt[j] > 0) cnt++;
					total += spCnt[j];
				}
				if (cnt < NspParam) continue;
				
				// If ALL, is the count the same as all members of the clusters?
				if (!bAny) {
					int numMem = dbc.executeCount(
						"SELECT count(*) FROM pog_members WHERE PGid= " + PGid.get(i));
					if (total != numMem) continue;
				}
				
				// add to list
				add++;
				if (retIdList==null) retIdList = "PGid IN (" + PGid.get(i);
				else retIdList += "," + PGid.get(i);
			}
			if (retIdList!=null) retIdList += ")";
			else {
				int c = dbc.executeCount("SELECT count(*) from pog_groups");
				c++;
				retIdList = "PGid=" + c; // force it to fail; otherwise all clusters will be displayed
			}
			rs.close();
			return retIdList;
		}
		catch (Exception e) {
			ErrorReport.die(e, "get List of Cluster IDs");
		}
		return null;
	}
	public String getSummary() {
		return summary;
	}
	public void clearAll() {
		ieDEcols.selectAll(false);
		
	}
	private IncludeExclude ieDEcols;
	
	private JTextField txtPval;
	private JTextField txtNspecies;
	private ButtonComboBox cmbAnyAll;
	private boolean hasNegDE;
	
	private int nSpecies;
	private ViewerFrame pFrame;
	private String summary;
	
	/*************** END main class ************************************/
	private JPanel firstPanel () {
		JPanel panel = createPanelPage();
		panel.add(Box.createVerticalStrut(5));
		// number of species
		JPanel row = createPanelLine();
		JLabel lblN = createLabel("At least ");
		txtNspecies = createTextField(nsp, 5);
		row.add(lblN);
		row.add(Box.createHorizontalStrut(5));
		row.add(txtNspecies);
		panel.add(row);
		
		lblN = createLabel(" species where ");
		row.add(lblN);
		row.add(Box.createHorizontalStrut(5));
		cmbAnyAll = new ButtonComboBox();
		cmbAnyAll.setBackground(Color.white);
		cmbAnyAll.addItem(any);
		cmbAnyAll.addItem(all);
		cmbAnyAll.setSelectedIndex(0);
		row.add(cmbAnyAll);
		row.add(Box.createHorizontalStrut(5));
		lblN = createLabel(" members per species must pass");
		row.add(lblN);
		row.add(Box.createHorizontalStrut(5));
		panel.add(row);
		panel.add(Box.createVerticalStrut(10));
		
		// p-value
		row = createPanelLine();
		JLabel lblPVal = createLabel("DE Pvalue < ");
		txtPval = createTextField(pval, 5);
		row.add(lblPVal);
		row.add(Box.createHorizontalStrut(5));
		row.add(txtPval);
		
		panel.add(row);						
		return panel;
	}
		
	/*****************************************************************
	 * XXX IncludeExclude panel (this should be a control)
	 */
	private class IncludeExclude extends JPanel {
		private static final long serialVersionUID = 3168533480285816618L;
		private static final int WIDTH = 100;
		
		public IncludeExclude(String [] nameList) {
			int nList = nameList.length * 2;
			if (hasNegDE) {
				theNameList = new String[nList];
				isInclude = new boolean[nList];
				
				for(int x=0; x<nameList.length; x++) {
					theNameList[x] = nameList[x];
					isInclude[x] = false;				
				}
				int offset = nameList.length;
				for(int x=0; x<nameList.length; x++) {
					theNameList[x+offset] = "-" + nameList[x];
					isInclude[x+offset] = false;				
				}
			}
			else {
				theNameList = new String[nameList.length];
				isInclude = new boolean[nameList.length];
				
				for(int x=0; x<nameList.length; x++) {
					theNameList[x] = nameList[x];
					isInclude[x] = false;				
				}
			}
			setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
			setAlignmentX(Component.LEFT_ALIGNMENT);
			setBackground(Color.white);
			
			//Initialize both list to empty, refresh when all is built
			includeList = new JList(emptyList);
			includeList.setEnabled(false);
			
			excludeList = new JList(emptyList);
			excludeList.setEnabled(false);
			
			includeList.setVisibleRowCount(4);
			excludeList.setVisibleRowCount(4);
			
			JScrollPane incPane = new JScrollPane(includeList);
			incPane.setAlignmentX(Component.LEFT_ALIGNMENT);
			JScrollPane exPane = new JScrollPane(excludeList);
			exPane.setAlignmentX(Component.LEFT_ALIGNMENT);
			
			JPanel incPanel = createPanelPage();
			JLabel incLabel = new JLabel("Filter on DE");
			incLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
			incPanel.add(incLabel);
			incPanel.add(incPane);
	
			JPanel exPanel = createPanelPage();
			JLabel exLabel = new JLabel("Pick from list");
			exLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
			exPanel.add(exLabel);
			exPanel.add(exPane);
	
			JPanel buttonPanel = createArrowPanel();
			
			Dimension d = new Dimension(WIDTH, buttonPanel.getPreferredSize().height);
			incPane.setPreferredSize(d);
			exPane.setPreferredSize(d);
	
			incPane.setMaximumSize(incPane.getPreferredSize());
			exPane.setMaximumSize(exPane.getPreferredSize());
	
			add(incPanel);
			add(Box.createHorizontalStrut(10));
			add(buttonPanel);
			add(Box.createHorizontalStrut(10));
			add(exPanel);	
	
			updateView();
		}
		
		private JPanel createArrowPanel() {
			JPanel retVal = createPanelPage();
			
			btnAddAllInc = createButton(">>");
			btnAddAllInc.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent arg0) {
					selectAll(false);
					updateView();
				}
			});
			
			btnAddInc = createButton(">");
			btnAddInc.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent arg0) {
					Object [] vals = includeList.getSelectedValues();
					for(int x=0; x<vals.length; x++)
						flipItem(vals[x]);
					updateView();
				}
			});
	
			btnAddEx = createButton("<");
			btnAddEx.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent arg0) {
					Object [] vals = excludeList.getSelectedValues();
					for(int x=0; x<vals.length; x++)
						flipItem(vals[x]);
					updateView();
				}
			});
			
			btnAddAllEx = createButton("<<");
			btnAddAllEx.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent arg0) {
					selectAll(true);
					updateView();
				}
			});
			
			//Thrown in to make the controls line up
			retVal.add(new JLabel(" "));
			retVal.add(btnAddAllInc);
			retVal.add(Box.createVerticalStrut(5));
			retVal.add(btnAddInc);
			retVal.add(Box.createVerticalStrut(5));
			retVal.add(btnAddEx);
			retVal.add(Box.createVerticalStrut(5));
			retVal.add(btnAddAllEx);
			
			retVal.setMaximumSize(retVal.getPreferredSize());
			retVal.setMinimumSize(retVal.getPreferredSize());
	
			return retVal;
		}	
		private void flipItem(Object item) {
			for(int x=0; x<theNameList.length; x++) {
				if(theNameList[x].equals(item)) {
					isInclude[x] = !isInclude[x];
					return;
				}
			}
		}	
		
		public void selectAll(boolean include) {
			for(int x=0; x<isInclude.length; x++)
				isInclude[x] = include;
			updateView();
		}					
		public int getNumIncluded() {
			int retVal = 0;
			for(int x=0; x<isInclude.length; x++)
				if(isInclude[x])
					retVal++;
			return retVal;
		}		
		public int getNumExcluded() {
			int retVal = 0;
			for(int x=0; x<isInclude.length; x++)
				if(!isInclude[x])
					retVal++;
			return retVal;
		}		
		public String [] includeList() {
			if(getNumIncluded() == 0) return null;
			
			String [] retVal = new String[getNumIncluded()];
			int index = 0;
			for(int x=0; x<theNameList.length; x++)
				if(isInclude[x]) {
					retVal[index] = theNameList[x];
					index++;
				}		
			return retVal;
		}		
		public String [] excludeList() {
			if(getNumExcluded() == 0) return null;
			
			String [] retVal = new String[getNumExcluded()];
			int index = 0;
			for(int x=0; x<theNameList.length; x++)
				if(!isInclude[x]) {
					retVal[index] = theNameList[x];
					index++;
				}
			
			return retVal;
		}
		
		private void updateView() {
			String [] incList = includeList();
			if(incList == null) {
				includeList.setListData(emptyList);
				includeList.setEnabled(false);
			} 
			else {
				includeList.setListData(incList);
				includeList.setEnabled(true);
			}
	
			String [] exList = excludeList();
			if(exList == null) {
				excludeList.setListData(emptyList);
				excludeList.setEnabled(false);
			} 
			else {
				excludeList.setListData(exList);
				excludeList.setEnabled(true);
			}
		}
		
		public void setEnabled(boolean enabled) {
			includeList.setEnabled(enabled);
			excludeList.setEnabled(enabled);
			btnAddAllInc.setEnabled(enabled);
			btnAddInc.setEnabled(enabled);
			btnAddEx.setEnabled(enabled);
			btnAddAllEx.setEnabled(enabled);
		}
		
		public boolean isEnabled() { return includeList.isEnabled(); }
		
		//Data holders
		private String [] theNameList = null;
		private boolean [] isInclude = null;
		private String [] emptyList = {"empty list"};
		
		//UI controls
		private JList includeList = null;
		private JList excludeList = null;
		private JButton btnAddAllInc = null;
		private JButton btnAddInc = null;
		private JButton btnAddEx = null;
		private JButton btnAddAllEx = null;
	} 
	/**********************************************
	 * Common functions
	 */
	static private JPanel createPanelLine() {
	    JPanel retVal = new JPanel();         
	    retVal.setBackground(Color.white);
	    retVal.setAlignmentX(Component.LEFT_ALIGNMENT);         
	    retVal.setLayout(new BoxLayout(retVal, BoxLayout.LINE_AXIS));             
	    return retVal;
	}
	static private JPanel createPanelPage() {
	    JPanel retVal = new JPanel();
	    retVal.setBackground(Color.white);
	    retVal.setAlignmentX(Component.LEFT_ALIGNMENT);         
	    retVal.setLayout(new BoxLayout(retVal, BoxLayout.PAGE_AXIS));
	    return retVal;
	}
	static private JButton createButton(String label) {
		JButton retVal = new JButton(label);
		retVal.setAlignmentX(Component.LEFT_ALIGNMENT);
		retVal.setBackground(Color.white);
		retVal.setMaximumSize(retVal.getPreferredSize());
		retVal.setPreferredSize(retVal.getPreferredSize());
		return retVal;
	}
	static private JTextField createTextField(String val, int size) {
		JTextField retVal =  new JTextField(val, size);
		retVal.setAlignmentX(Component.LEFT_ALIGNMENT);
		retVal.setBackground(Color.white);
		retVal.setMaximumSize(retVal.getPreferredSize());
		retVal.setPreferredSize(retVal.getPreferredSize());
		return retVal;
	}
	static public JLabel createLabel(String val) {
		JLabel retVal =  new JLabel(val);
		retVal.setAlignmentX(Component.LEFT_ALIGNMENT);
		retVal.setBackground(Color.white);
		return retVal;
	}
}
